package com.hydong.novatest.Utils;

import net.sourceforge.novaforjava.api.LnDate;
import net.sourceforge.novaforjava.api.LnDms;
import net.sourceforge.novaforjava.api.LnEquPosn;
import net.sourceforge.novaforjava.api.LnHms;
import net.sourceforge.novaforjava.api.LnHrzPosn;
import net.sourceforge.novaforjava.api.LnLnlatPosn;
import net.sourceforge.novaforjava.api.LnhEquPosn;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import static net.sourceforge.novaforjava.JulianDay.ln_get_julian_day;
import static net.sourceforge.novaforjava.Precession.ln_get_equ_prec;
import static net.sourceforge.novaforjava.Transform.ln_get_hrz_from_equ;
import static net.sourceforge.novaforjava.Utility.ln_equ_to_hequ;
import static net.sourceforge.novaforjava.Utility.ln_hequ_to_equ;

/**
 * 天体计算工具类
 * 基于 novaforjava 库
 */
public class StartTools {

    private static StartTools instance;

    // 当前儒略日
    private static double JD = 0d;

    // 天体观测的地理位置
    private static LnLnlatPosn localPosition;

    private StartTools() {
    }

    public static StartTools getInstance() {
        if (instance == null) {
            instance = new StartTools();

            JD = getJD();

            // 默认位置（苏州坐标）
            localPosition = new LnLnlatPosn();
            localPosition.lng = 120.73765885860443;
            localPosition.lat = 31.26636927574242;

        }
        return instance;
    }

    /**
     * 更改观察点地理位置
     *
     * @param lng 经度 longitude
     * @param lat 纬度 latitude
     */
    public static void setLocalPosition(double lng, double lat) {
        localPosition = new LnLnlatPosn();
        localPosition.lng = lng;
        localPosition.lat = lat;
    }

    /**
     * 把 JNOW Double数据 转成 JNOW(HMS/DMS)数据
     *
     * @param jNow
     * @return
     */
    public static LnhEquPosn formatJNow(LnEquPosn jNow) {
        LnhEquPosn output = new LnhEquPosn();
        ln_equ_to_hequ(jNow, output);
        return output;
    }

    /**
     * 把 原始数值 转成 JNOW Double数据
     *
     * @param raDouble  原始数值（十进制）
     * @param decDouble 原始数值（十进制）
     * @return
     */
    public static LnEquPosn transJ200ToJNow(double raDouble, double decDouble) {
        LnhEquPosn lnhEquPosn = new LnhEquPosn();

        int hours = (int) raDouble;
        int minutes = (int) (((raDouble - hours) * 3600) / 60);
        double seconds = (((raDouble - hours) * 3600) - minutes * 60);
        LnHms ra = new LnHms();
        ra.hours = hours;
        ra.minutes = minutes;
        ra.seconds = seconds;

        double absDec = Math.abs(decDouble);
        int degrees = (int) absDec;
        int minute = (int) (((absDec - degrees) * 3600) / 60);
        double second = (((absDec - degrees) * 3600) - minute * 60);
        int neg = decDouble > 0 ? 0 : 1; // 零为正数，非零为负数
        LnDms dec = new LnDms();
        dec.neg = neg;
        dec.degrees = degrees;
        dec.minutes = minute;
        dec.seconds = second;

        // 将 原始数值（十进制） 转成 J2000(HMS/DMS)数据
        lnhEquPosn.ra = ra;
        lnhEquPosn.dec = dec;

        // 将 J2000(HMS/DMS)数据 转成 J2000 Double数据
        LnEquPosn j2000 = new LnEquPosn();
        ln_hequ_to_equ(lnhEquPosn, j2000);

        // 将 J2000 Double数据 转成 JNOW Double数据
        LnEquPosn jNow = new LnEquPosn();
        ln_get_equ_prec(j2000, JD, jNow);
        return jNow;
    }

    /**
     * 计算天体的 方位 和 高度角
     *
     * @param jnow 目标天体的 Jnow 数值
     * @return
     */
    public static List<LnHrzPosn> calAltitudeList(LnEquPosn jnow) {
        List<LnHrzPosn> altitudeList = new ArrayList<>();
        for (int i = 0; i < 13; i++) {
            int hour = i + 18;
            if (hour > 24) {
                hour -= 24;
            }
            LnHrzPosn lnHrzPosn = new LnHrzPosn();
            ln_get_hrz_from_equ(jnow, localPosition, getJDByHour(hour), lnHrzPosn);
            altitudeList.add(lnHrzPosn);
        }
        return altitudeList;
    }

    /**
     * 计算 当前 儒略日
     */
    public static double getJD() {
        Calendar now = Calendar.getInstance();
        int zoneOffset = now.get(Calendar.ZONE_OFFSET);
        int dstOffset = now.get(Calendar.DST_OFFSET);
        now.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));

        LnDate lnDate = new LnDate();
        lnDate.years = now.get(Calendar.YEAR);
        lnDate.months = now.get(Calendar.MONTH) + 1;
        lnDate.days = now.get(Calendar.DAY_OF_MONTH);
        lnDate.hours = now.get(Calendar.HOUR_OF_DAY);
        lnDate.minutes = now.get(Calendar.MINUTE);
        lnDate.seconds = now.get(Calendar.SECOND);

        return ln_get_julian_day(lnDate);
    }

    /**
     * 计算 整时 儒略日
     */
    private static double getJDByHour(int hourOfDay) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);

        int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
        int dstOffset = calendar.get(Calendar.DST_OFFSET);
        calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));

        LnDate lnDate = new LnDate();
        lnDate.years = calendar.get(Calendar.YEAR);
        lnDate.months = calendar.get(Calendar.MONTH) + 1;
        lnDate.days = calendar.get(Calendar.DAY_OF_MONTH);
        lnDate.hours = calendar.get(Calendar.HOUR_OF_DAY);
        lnDate.minutes = calendar.get(Calendar.MINUTE);
        lnDate.seconds = calendar.get(Calendar.SECOND);

        return ln_get_julian_day(lnDate);
    }
}
